﻿using System;
using Microsoft.Xna.Framework;

namespace Black.Surface2DEngine.Graphic2D.Render2D.Process2D
{
    /// <summary>
    /// Gère un tremblement pour un objet 2D
    /// </summary>
    public sealed class Process2DShake: Process2D
    {
        #region Variables d'instances

        private bool m_bOnX;
        private bool m_bOnY;
        private int m_iAmplitude;
        private float m_fShakeTime;
        private float m_fInterval;
        private float m_fIntervalTime;
        private Vector2 m_vTranslateSrc;

        #endregion

        #region Constructeurs et destructeur

        /// <summary>
        /// Constructeur de Process2DShake
        /// </summary>
        /// <param name="p_oObject">Objet sur qui on fait le process</param>
        /// <param name="p_fAmplitude">Amplitude du tremblement en pixel</param>
        /// <param name="p_fInterval">Interval en milliseconde entre chaque mouvement</param>
        /// <param name="p_bOnX">TRUE tremblement sur l'axe X, FALSE sinon</param>
        /// <param name="p_bOnY">TRUE tremblement sur l'axe Y, FALSE sinon</param>
        /// <param name="p_fTime">Temps pour effectuer le tremblement en milliseconde</param>
        /// <param name="p_bLoop">TRUE redémare le process à la fin, FALSE sinon</param>
        /// <param name="p_iLoopOccurs">Indique combien de fois il doit boucler, NULL si boucle infinie</param>
        public Process2DShake(Entity2D p_oObject, float p_fAmplitude, float p_fInterval, bool p_bOnX, bool p_bOnY, float p_fTime, bool p_bLoop, int? p_iLoopOccurs)
            : base(p_oObject, p_bLoop, false, p_iLoopOccurs)
        {
            // Initialisation

            this.m_bOnX = p_bOnX;
            this.m_bOnY = p_bOnY;
            this.m_iAmplitude = Convert.ToInt32(p_fAmplitude);
            this.m_fInterval = p_fInterval;
            this.m_fShakeTime = p_fTime;
            this.m_vTranslateSrc = p_oObject.Position;

            // On démare le processus

            this.LoopStart();

            // Si temps 0 on l'arrête

            if ( this.m_fShakeTime < 1.0f )
                this.End();
        }

        #endregion

        #region Fonctions publiques

        /// <summary>
        /// Met à jour les informations de la translation
        /// </summary>
        /// <param name="p_fTime">Information temporelle</param>
        public sealed override void Update(float p_fTime)
        {
            // Mise à jour des temps

            this.m_fTime += p_fTime;
            this.m_fIntervalTime += p_fTime;

            // Vérification fin

            if ( this.m_fTime >= this.m_fShakeTime )
                this.InternalEnd();
            else
            {
                // Déplacement ?

                if ( this.m_fIntervalTime >= this.m_fInterval )
                {
                    // Réinit des compteurs

                    this.m_fIntervalTime = 0.0f;

                    // Création de la classe random pour l'amplitude

                    float fDeplacement;
                    Random oRand = new Random();

                    // Détermination en X

                    if ( this.m_bOnX )
                    {
                        fDeplacement = Convert.ToSingle(oRand.Next(0, this.m_iAmplitude));

                        if ( oRand.Next(-100, 100) < 0 )
                            this.m_oEntity.X = this.m_vTranslateSrc.X + (fDeplacement * -1.0f);
                        else
                            this.m_oEntity.X = this.m_vTranslateSrc.X + fDeplacement;
                    }

                    // Détermination en Y

                    if ( this.m_bOnY )
                    {
                        fDeplacement = Convert.ToSingle(oRand.Next(0, this.m_iAmplitude));

                        if ( oRand.Next(-100, 100) < 0 )
                            this.m_oEntity.Y = this.m_vTranslateSrc.Y + (fDeplacement * -1.0f);
                        else
                            this.m_oEntity.Y = this.m_vTranslateSrc.Y + fDeplacement;
                    }
                }
            }
        }

        #endregion

        #region Fonctions internes

        /// <summary>
        /// Effectue des traitements à la fin d'une occurence du process 2D
        /// </summary>
        internal sealed override void LoopEnd()
        {
            this.m_oEntity.X = this.m_vTranslateSrc.X;
            this.m_oEntity.Y = this.m_vTranslateSrc.Y;
        }

        /// <summary>
        /// Relance le loop
        /// </summary>
        internal sealed override void LoopStart()
        {
            // Véfification

            if ( this.m_oEntity == null )
            {
                this.m_bProcess = false;
                return;
            }

            // Mise à jour indicateur de progression

            this.m_fTime = 0.0f;
            this.m_fIntervalTime = 0.0f;
            this.m_bProcess = true;
        }

        #endregion

        #region Indexer

        /// <summary>
        /// Retourne le type de l'effet 2D
        /// </summary>
        public sealed override Effect2D Effect
        {
            get
            {
                return (Effect2D.Shake);
            }
        }

        #endregion
    }
}
